package drds.plus.sql_process.optimizer.chooser.share_delegate;

import drds.plus.sql_process.abstract_syntax_tree.node.Node;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 获取{@linkplain Node}的代理对象
 *
 * <pre>
 * 主要为解决Merge下node的节点共享问题，TableNode经过规则计算会生成多个基本相同的的分库分表节点
 * 而每个节点唯一不同的主要是group/tableName/extra，内存占用和节点树build代价都会比较高.
 * 使用代理技术，运行过程中实际只有一个KvIndexNode,分库分表生成的节点都是代理到该节点上，同时持有一个shareIndex下标进行group/tableName数据的查找
 * </pre>
 */
public class ShareDelegate<T extends Node> {

    public static Map<String, Class> delegateClassNameToProxyClassMap = new ConcurrentHashMap<String, Class>();
    public T delegateObject;
    public Class delegateClass;
    public int shareIndex;

    public ShareDelegate(T delegateObject, int shareIndex) {
        this.delegateObject = delegateObject;
        this.delegateClass = delegateObject.getClass();
        this.shareIndex = shareIndex;
    }

    /**
     * 注册对应的proxyClass到仓库中
     */
    public static void registerProxyClass(String delegateClassName, Class proxyClass) {
        if (!delegateClassNameToProxyClassMap.containsKey(delegateClassName)) { // 避免重复提交
            synchronized (delegateClassNameToProxyClassMap) {
                if (!delegateClassNameToProxyClassMap.containsKey(delegateClassName)) {
                    delegateClassNameToProxyClassMap.put(delegateClassName, proxyClass);
                }
            }
        }
    }

    /**
     * 如果存在对应的key的ProxyClass就返回，没有则返回null
     */
    public static Class getProxyClass(String delegateClassName) {
        return delegateClassNameToProxyClassMap.get(delegateClassName);
    }

    public T getProxyClass() {
        Class proxyClass = getProxyClass(delegateClass.getName());
        if (proxyClass == null) {
            Enhancer enhancer = new Enhancer();
            if (delegateClass.isInterface()) { // 判断是否为接口，优先进行接口代理可以解决service为final
                enhancer.setInterfaces(new Class[]{delegateClass});
            } else {
                enhancer.setSuperclass(delegateClass);
            }
            enhancer.setCallbackTypes(new Class[]{DirectDispatcher.class, ShareDelegateMethodInterceptor.class});
            enhancer.setCallbackFilter(new CallbackFilter() {
                public int accept(Method method) {
                    if (method.getAnnotation(ShareDelegateAnnotation.class) == null) {
                        return 0; // 直接转发,使用ProxyDirect
                    } else {
                        return 1; // 需要被代理，使用ProxyInterceptor
                    }
                }
            });
            proxyClass = enhancer.createClass();
            // 注册proxyClass
            registerProxyClass(delegateClass.getName(), proxyClass);
        }

        Enhancer.registerCallbacks(proxyClass, new Callback[]{new DirectDispatcher(this), new ShareDelegateMethodInterceptor(this)});
        try {
            Constructor constructor = proxyClass.getConstructor();// 先尝试默认的空构造函数
            return (T) constructor.newInstance(new Object[0]);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            // clear thread callbacks to allow them to be gc'd
            Enhancer.registerStaticCallbacks(proxyClass, null);
        }
    }

}
